use crate::model::indicators::boll::BOLL;
use crate::model::indicators::macd::MACD;
use crate::model::indicators::rsi::RSI;
use crate::model::indicators::IndicatorActionWrap;

use super::IndicatorHistory;

/// 1. Define Signal Weights
/// First, establish a weight for each type of signal, considering their significance in your strategy.
const BUY_WEIGHT: f64 = 2.0;
const SELL_WEIGHT: f64 = -1.5; // Negative to reflect selling action
const THRESHOLD_BUY: f64 = 80.0;
const THRESHOLD_SELL: f64 = 20.0;

/// 1.1 Define Signal Type Importance
/// First, define a mapping or function that assigns a modifier to each SignalType. For simplicity, let's assume higher SignalType values indicate greater importance.
/// Mapping example: Higher SignalType implies stronger signal
/// Modifier based on SignalType importance
fn signal_type_weight(signal_type: u8) -> f64 {
    // This is a hypothetical mapping, adjust according to your strategy
    match signal_type {
        1..=10 => 1.0,       // Base level of importance
        11..=20 => 1.2,      // Slightly more important
        21..=u8::MAX => 1.5, // Most important signals
        _ => 1.0,            // Default case, could be error handling too
    }
}

/// 2. Handling Numeric Indicators
/// For numeric indicators like macd_v, signal_v, etc., after standardization, apply a scoring mechanism.
/// This could be a simple threshold-based system or a more complex one depending on your strategy.
///
fn calculate_numeric_score(
    value: f64,
    positive_threshold: f64,
    negative_threshold: f64,
    above_positive_weight: f64,
    below_negative_weight: f64,
) -> f64 {
    if value > positive_threshold {
        above_positive_weight
    } else if value < negative_threshold {
        below_negative_weight
    } else {
        0.0
    }
}

/// 3. Compute Historical Signal Strength
/// Design a method to evaluate the cumulative strength or tendency of signals over the window.
/// This could involve counting the number of buy/sell signals, averaging the ratio() values,
/// or applying more complex logic based on your strategy.
///
/// @param signals: Vec<IndicatorActionWrap>
///
/// Returns a value between -1.0 and 1.0
///
fn historical_signal_tendency(signals: &[Option<IndicatorActionWrap>]) -> Option<f64> {
    let buy_count = signals
        .iter()
        .filter(|&x| if let Some(x) = x { x.buy > 0 } else { false })
        .count() as f64;
    let sell_count = signals
        .iter()
        .filter(|&x| if let Some(x) = x { x.sell > 0 } else { false })
        .count() as f64;
    let total = signals.len() as f64;

    if total > 0.0 {
        Some((buy_count - sell_count) / total) // Returns a value between -1.0 and 1.0
    } else {
        None
    }
}

/// 4. Aggregate Scores
///
/// compute scores from the stored indicators
///
impl ScoreAggregator {
    pub fn new(
        macds: Option<IndicatorHistory<MACD>>,
        rsis: Option<IndicatorHistory<RSI>>,
        bolls: Option<IndicatorHistory<BOLL>>,
    ) -> Self {
        ScoreAggregator {
            macds,
            rsis,
            bolls,
            // Initialize other indicators if added
        }
    }

    /// 5. Decision Thresholds
    /// Determine buy/sell/hold decisions based on the total score.
    pub fn decide_action(&self) -> Option<IndicatorActionWrap> {
        let total_score = self.compute_total_scores();
        if total_score > THRESHOLD_BUY {
            Some(IndicatorActionWrap {
                buy: u8::MAX as i64,
                sell: 0,
            })
        } else if total_score < THRESHOLD_SELL {
            Some(IndicatorActionWrap {
                sell: u8::MAX as i64,
                buy: 0,
            })
        } else {
            None
        }
    }

    // Method to compute scores from the stored indicators
    fn compute_total_scores(&self) -> f64 {
        let macd_score = self.compute_macds_score();
        let rsi_score = self.compute_rsis_score();
        let boll_score = self.compute_bolls_score();

        // Implement logic to combine these scores, e.g., a weighted sum
        macd_score + rsi_score + boll_score
    }

    // Individual methods to compute scores from each indicator
    fn compute_macds_score(&self) -> f64 {
        let mut signals_tendency = 0.0;

        // Implement logic to derive a score from MACD data
        // Example: use self.macd.signal0, self.macd.bar_v, etc.
        if let Some(mds) = &self.macds {
            signals_tendency = historical_signal_tendency(mds.signals()).unwrap_or(0.0);
        }
        signals_tendency + 0.0 // Placeholder for actual computation
    }

    fn compute_rsis_score(&self) -> f64 {
        let mut signals_tendency = 0.0;

        // Implement logic to derive a score from RSI data
        if let Some(mds) = &self.rsis {
            signals_tendency = historical_signal_tendency(mds.signals()).unwrap_or(0.0);
        }
        signals_tendency + 0.0 // Placeholder for actual computation
    }

    fn compute_bolls_score(&self) -> f64 {
        let mut signals_tendency = 0.0;

        // Implement logic to derive a score from BOLL data
        if let Some(mds) = &self.bolls {
            signals_tendency = historical_signal_tendency(mds.signals()).unwrap_or(0.0);
        }
        signals_tendency + 0.0 // Placeholder for actual computation
    }
}

/// 4. Aggregate Scores
pub struct ScoreAggregator {
    macds: Option<IndicatorHistory<MACD>>,
    rsis: Option<IndicatorHistory<RSI>>,
    bolls: Option<IndicatorHistory<BOLL>>,
    // Add more fields as needed for other indicators
}
pub struct ScoreAggregatorBuilder {
    macd: Option<IndicatorHistory<MACD>>,
    rsi: Option<IndicatorHistory<RSI>>,
    boll: Option<IndicatorHistory<BOLL>>,
    // Add more fields as needed for other indicators
}

impl ScoreAggregatorBuilder {
    pub fn new() -> Self {
        ScoreAggregatorBuilder {
            macd: None,
            rsi: None,
            boll: None,
            // Initialize other indicators as None
        }
    }

    pub fn with_macd(mut self, macd: IndicatorHistory<MACD>) -> Self {
        self.macd = Some(macd);
        self
    }

    pub fn with_rsi(mut self, rsi: IndicatorHistory<RSI>) -> Self {
        self.rsi = Some(rsi);
        self
    }

    pub fn with_boll(mut self, boll: IndicatorHistory<BOLL>) -> Self {
        self.boll = Some(boll);
        self
    }

    // Add methods for other indicators...

    pub fn build(self) -> ScoreAggregator {
        let macd = self.macd;
        let rsi = self.rsi;
        let boll = self.boll;

        ScoreAggregator {
            macds: macd,
            rsis: rsi,
            bolls: boll,
            // Initialize other indicators if added
        }
    }
}

/// 3. Incorporate Historical Trends into Today's Score
/// When computing today's total score, consider both today's signal and the historical tendency.
//fn calculate_today_score(
//    today_macd: IndicatorActionWrap,
//    today_boll: IndicatorActionWrap,
//    macd_hist: &IndicatorHistory<IndicatorActionWrap>,
//    boll_hist: &IndicatorHistory<IndicatorActionWrap>,
//) -> f64 {
//    let today_macd_ratio = today_macd.ratio().unwrap_or(0.0); // Or handle None appropriately
//    let today_boll_ratio = today_boll.ratio().unwrap_or(0.0);
//
//    let historical_macd_tendency = historical_signal_tendency(macd_hist.signals()).unwrap_or(0.0);
//    let historical_boll_tendency = historical_signal_tendency(boll_hist.signals()).unwrap_or(0.0);
//
//    // Combine today's signal strength with historical tendencies, adjusting weights as per strategy
//    let total_score = today_macd_ratio * 0.6 + // Weigh today's MACD signal
//        historical_macd_tendency * 0.2 + // Add historical MACD trend
//        today_boll_ratio * 0.6 + // Weigh today's BOLL signal
//        historical_boll_tendency * 0.2; // Add historical BOLL trend
//
//    total_score
//}

/// 4. Update and Reassess Daily
/// Each day, after receiving new signals, update the historical windows and recalculate the total score based on the updated information.
// macd_history.push(today_macd);
// boll_history.push(today_boll);
// let total_score_today = calculate_today_score(today_macd, today_boll, &macd_history, &boll_history);
// By including historical context, you're introducing a degree of smoothing that can help filter out noise and make your trading decisions more robust against short-term fluctuations. As always, the specifics of how you weigh today's signals versus historical tendencies should be based on backtesting and aligned with your trading strategy's objectives and risk tolerance.

#[cfg(test)]
mod test {

    #[tokio::test]
    async fn test_compute_total_scores() {

        //    let macd = calculate_macd(/* parameters */);
        //    let rsi = calculate_rsi(/* parameters */);
        //    let boll = calculate_boll(/* parameters */);
        //
        //    let score_aggregator = ScoreAggregatorBuilder::new()
        //        .with_macd(macd)
        //        .with_rsi(rsi)
        //        .with_boll(boll)
        //        // Add more indicators as needed
        //        .build();
        //
        //    let total_score = score_aggregator.compute_scores();
        //
        //    println!("Total Score: {}", total_score);
    }
}
